﻿/******************************************************************************
 * Copyright (C) 671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "AuthSession.h"
#include <boost/unordered_map.hpp>
#include <NodeCpp/AutoPtr.h>
#include <NodeCpp/Util/Application.h>
#include "Shared/Log/Log.h"
#include "GlobalVars.h"
#include "RealmList.h"
#include "AuthSocketDelegate.h"
#include "AuthSessionManager.h"
using NodeCpp::AutoPtr;

#define s_BYTE_SIZE 32

class LogonChallengeQueryHolder : public SqlQueryHolder
{
public:
    enum QueryIndex
    {
        QUERY_INDEX_IP_BANNED = 0,
        QUERY_INDEX_ACCOUNT,
        QUERY_INDEX_ACCOUNT_BANNED,
        MAX_QUERY
    };

    LogonChallengeQueryHolder(int _ID) : ID_(_ID) { }
    ~LogonChallengeQueryHolder(void) { }

    int GetID(void) const { return ID_; }

private:
    int ID_;
};

class AuthHandler
{
public:
    void HandleLogonChallengeCallback(QueryResult * /*dummy*/, SqlQueryHolder* _Holder)
    {
        AutoPtr<SqlQueryHolder> _HolderPtr(_Holder);
        AuthSession* _Session = sAuthSessionMgr.FindSession(((LogonChallengeQueryHolder*)_Holder)->GetID());
        _Session->HandleLogonChallenge(_Holder);
    }

    void HandleReconnectChallengeCallback(QueryResult *_Result, int _ID)
    {
        AutoPtr<QueryResult> _ResultPtr(_Result);
        AuthSession* _Session = sAuthSessionMgr.FindSession(_ID);
        _Session->HandleReconnectChallenge(_Result);
    }

    void HandleFetchCharaNumCallback(QueryResult *_Result, int _ID)
    {
        AutoPtr<QueryResult> _ResultPtr(_Result);
        AuthSession* _Session = sAuthSessionMgr.FindSession(_ID);
        _Session->HandleRealmList(_Result);
    }
}G_AuthHandler_;

AuthSession::AuthSession(AuthSocketDelegate* _Sock)
    : Sock_(_Sock)
    , Packets_()
    , Running_(true)

    , PeerID_(0)
    , PeerHost_()
    , PeerPort_(0)

    , Authed_(false)

    , AccountId_(0)
    , LoginName_()
    , SafeLoginName_()
    , Build_(0)
    , LocalizationName_()
    , AccountsecLv_(SEC_PLAYER)

    , N_(), s_(), g_(), v_()
    , b_(), B_()
    , K_()
    , ReconnectProof_()

    , FailedLogins_(0)
{
    PeerID_ = Sock_->GetPeerID();
    PeerHost_ = Sock_->GetPeerHost();
    PeerPort_ = Sock_->GetPeerPort();

    // 未通过验证可以存活
    ::time(&UnauthLifeTime_);
    UnauthLifeTime_ += sAuthSessionMgr.GetMaxUnauthedTime();
    UnauthLifeTime_ *= 1000;

    N_.SetHexStr(SRP6::N);
    g_.SetUInt32(SRP6::g);
}

AuthSession::~AuthSession(void)
{
    _Uninitialize();
}

void AuthSession::QueuePacket(AuthPacket* _Packet) {
    Packets_.Add(_Packet);
}

bool AuthSession::Update(time_t _Diff)
{
    if (!Running_) {
        return !Sock_->TryClose();
    }

    do
    {
        // 验证超时
        if (!Authed_) {
            UnauthLifeTime_ -= _Diff;
            if (UnauthLifeTime_ < 0) {
                Running_ = false;
                break;
            }
        }

        // 服务器正在关闭或网络出错
        if (!sAuthSessionMgr.IsRunning()
            || Sock_->IsClosing()) {
                Running_ = false;
                break;
        }

        // 处理数据包
        AuthPacket* _Pkt;
        while (Packets_.Next(_Pkt)) {
            if (!HandlePacket(_Pkt)) {
                Running_ = false;
                break;
            }
            SAFE_DEL(_Pkt);
        }
    } while (false);

    return true;
}

bool AuthSession::HandlePacket(AuthPacket* _Packet)
{
    switch(_Packet->opcode())
    {
    case CMD_AUTH_LOGON_CHALLENGE:
        sLog.outDebug("(%d) Received CMD_AUTH_LOGON_CHALLENGE", PeerID_);
        return HandleLogonChallenge(_Packet);
    case CMD_AUTH_LOGON_PROOF:
        sLog.outDebug("(%d) Received CMD_AUTH_LOGON_PROOF", PeerID_);
        return HandleLogonProof(_Packet);
    case CMD_AUTH_RECONNECT_CHALLENGE:
        sLog.outDebug("(%d) Received CMD_AUTH_RECONNECT_CHALLENGE", PeerID_);
        return HandleReconnectChallenge(_Packet);
    case CMD_AUTH_RECONNECT_PROOF:
        sLog.outDebug("(%d) Received CMD_AUTH_RECONNECT_PROOF", PeerID_);
        return HandleReconnectProof(_Packet);
    case CMD_REALM_LIST:
        sLog.outDebug("(%d) Received CMD_REALM_LIST", PeerID_);
        return HandleRealmList(_Packet);

    default:
        return false;
    }
}

bool AuthSession::HandleLogonChallenge(AuthPacket* _Packet)
{
    sAuthLogonChallenge_C* _Ch = (sAuthLogonChallenge_C*)_Packet->contents();
    ByteBuffer _RespPkt;

    LoginName_.assign((const char*)_Ch->I, _Ch->I_len);
    Build_ = _Ch->Build;
    SafeLoginName_ = LoginName_;
    sLoginDB.EscapeString(SafeLoginName_);

    LocalizationName_.resize(4);
    for (int i = 0; i < 4; ++i) {
        LocalizationName_[i] = _Ch->Country[4 - i - 1];
    }

    sLog.outDebug("%d account %s is using '%c%c%c%c' locale (%u)", PeerID_,
        LoginName_.c_str(), _Ch->Country[3], _Ch->Country[2],
        _Ch->Country[1], _Ch->Country[0], GetLocaleByName(LocalizationName_));

    std::string _Address = PeerHost_;
    sLoginDB.EscapeString(_Address);

    /* 查询帐号数据 */
    AutoPtr<LogonChallengeQueryHolder> _QueryHolder(new LogonChallengeQueryHolder(PeerID_));
    if (!_QueryHolder) {
        return false;
    }
    _QueryHolder->SetSize(LogonChallengeQueryHolder::MAX_QUERY);

    bool _SetQueryRes = _QueryHolder->SetPQuery(LogonChallengeQueryHolder::QUERY_INDEX_IP_BANNED,
        "SELECT unbandate FROM ip_banned WHERE "
        //    permanent                    still banned
        "(unbandate = bandate OR unbandate > UNIX_TIMESTAMP()) AND ip = '%s'", _Address.c_str());
    _SetQueryRes &= _QueryHolder->SetPQuery(LogonChallengeQueryHolder::QUERY_INDEX_ACCOUNT,
        "SELECT sha_pass_hash,id,locked,last_ip,gmlevel,v,s,failed_logins FROM account WHERE username = '%s'", SafeLoginName_.c_str());
    _SetQueryRes &= _QueryHolder->SetPQuery(LogonChallengeQueryHolder::QUERY_INDEX_ACCOUNT_BANNED,
        "SELECT bandate,unbandate FROM account_banned WHERE "
        "account = '%s' AND active = 1 AND (unbandate > UNIX_TIMESTAMP() OR unbandate = bandate)", SafeLoginName_.c_str());

    if (!_SetQueryRes) {
        return false;
    }

    sLoginDB.DelayQueryHolder(&G_AuthHandler_, &AuthHandler::HandleLogonChallengeCallback, _QueryHolder.release());
    return true;
}

void AuthSession::HandleLogonChallenge(SqlQueryHolder* _Holder)
{
    LogonChallengeQueryHolder* _QueryHolder = (LogonChallengeQueryHolder*)_Holder;
    ByteBuffer _Pkt;

    _Pkt << (std::uint8_t) CMD_AUTH_LOGON_CHALLENGE;
    _Pkt << (std::uint8_t) 0x00;

    do
    {
        /* 检查IP是否被禁止登录 */
        QueryResult* _Result = _QueryHolder->GetResult(LogonChallengeQueryHolder::QUERY_INDEX_IP_BANNED);
        if (_Result)
        {
            _Pkt << (std::uint8_t)WOW_FAIL_BANNED;
            sLog.outDebug("%d Banned ip %s tries to login!", PeerID_, PeerHost_.c_str());
            break;
        }

        /* 获取帐号数据 */
        _Result = _QueryHolder->GetResult(LogonChallengeQueryHolder::QUERY_INDEX_ACCOUNT);
        if (!_Result) {
            _Pkt << (std::uint8_t) WOW_FAIL_UNKNOWN_ACCOUNT;
            sLog.outDebug("%d Unknown account %s tries to login!", PeerID_, LoginName_.c_str());
            break;
        }

        /* 检查帐号是否锁定登录IP，如果锁定检查当前登录IP是否合法 */
        bool _Locked = false;
        if ((*_Result)[2].AsUInt8() == 1)
        {
            sLog.outDebug("%d Account '%s' is locked to IP - '%s'", PeerID_, LoginName_.c_str(), (*_Result)[3].c_str());
            sLog.outDebug("%d Player address is '%s'", PeerID_, PeerHost_.c_str());
            if (strcmp((*_Result)[3].c_str(), PeerHost_.c_str()))
            {
                sLog.outDebug("%d Account IP differs", PeerID_);
                _Pkt << (std::uint8_t) WOW_FAIL_SUSPENDED;
                _Locked = true;
            }
            else {
                sLog.outDebug("%d Account IP matches", PeerID_);
            }
        }
        else {
            sLog.outDebug("%d Account '%s' is not locked to ip", PeerID_, LoginName_.c_str());
        }
        if (_Locked) {
            break;
        }

        /* 检查帐号是否被禁止登录 */
        QueryResult* _BanResult = _QueryHolder->GetResult(LogonChallengeQueryHolder::QUERY_INDEX_ACCOUNT_BANNED);
        if (_BanResult)
        {
            if ((*_BanResult)[0].AsUInt64() == (*_BanResult)[1].AsUInt64())
            {
                _Pkt << (std::uint8_t) WOW_FAIL_BANNED;
                sLog.outDebug("%d Banned account %s tries to login!", PeerID_, LoginName_.c_str());
            }
            else
            {
                _Pkt << (std::uint8_t) WOW_FAIL_SUSPENDED;
                sLog.outDebug("%d Temporarily banned account %s tries to login!", PeerID_, LoginName_.c_str());
            }
            break;
        }

        AccountId_ = (*_Result)[1].AsUInt32();
        FailedLogins_ = (*_Result)[7].AsUInt32();

        // * SRP --------------------------------------------------
        std::string _RI = (*_Result)[0].AsString();

        /* 从数据库读取v,s的值 */
        std::string _DbV = (*_Result)[5].AsString();
        std::string _DbS = (*_Result)[6].AsString();
        sLog.outDebug("%d database authentication values: v='%s' s='%s'", PeerID_, _DbV.c_str(), _DbS.c_str());

        /* 如果数据库没有保存v,s或者保存的v,s不符合要求则重新计算v,s */
        if (_DbV.size() != s_BYTE_SIZE * 2 || _DbS.size() != s_BYTE_SIZE * 2) {
            _SetVSFields(_RI);
        }
        else {
            s_.SetHexStr(_DbS.c_str());
            v_.SetHexStr(_DbV.c_str());
        }

        /* 计算B */
        b_.SetRand(19 * 8);
        BigNumber _Gmod = g_.ModExp(b_, N_);
        B_ = ((v_ * SRP6::k) + _Gmod) % N_;
        NODECPP_ASSERT(_Gmod.GetNumBytes() <= 32);

        BigNumber _Unk3;
        _Unk3.SetRand(16 * 8);

        _Pkt << (std::uint8_t)WOW_SUCCESS;
        _Pkt.Append(B_.AsByteArray(32), 32);
        _Pkt << (std::uint8_t)1;
        _Pkt.Append(g_.AsByteArray(), 1);
        _Pkt << (std::uint8_t)32;
        _Pkt.Append(N_.AsByteArray(32), 32);
        _Pkt.Append(s_.AsByteArray(), s_.GetNumBytes());
        _Pkt.Append(_Unk3.AsByteArray(16), 16);

        std::uint8_t _SecurityFlags = 0;
        _Pkt << (std::uint8_t)_SecurityFlags;
        if (_SecurityFlags & 0x01) {
            _Pkt << std::uint32_t(0);
            _Pkt << std::uint64_t(0) << std::uint64_t(0);
        }

        if (_SecurityFlags & 0x02) {
            _Pkt << std::uint8_t(0);
            _Pkt << std::uint8_t(0);
            _Pkt << std::uint8_t(0);
            _Pkt << std::uint8_t(0);
            _Pkt << std::uint64_t(0);
        }
        if (_SecurityFlags & 0x04) {
            _Pkt << std::uint8_t(1);
        }

        /* 解析帐号等级 */
        std::uint8_t _SecLevel = (*_Result)[4].AsUInt8();
        AccountsecLv_ = (_SecLevel <= SEC_ADMINISTRATOR ? AccountTypes(_SecLevel) : SEC_ADMINISTRATOR);
    } while (false);

    Sock_->SendPacket(_Pkt);
}

bool AuthSession::HandleLogonProof(AuthPacket* _Packet)
{
    sAuthLogonProof_C* _Lp = (sAuthLogonProof_C*)_Packet->contents();
    bool _Error = false;

    do
    {
        /* 检查版本支持 */
        if (!IsAcceptableClientBuild(Build_))
        {
            ByteBuffer _Pkt;
            _Pkt << (std::uint8_t) CMD_AUTH_LOGON_CHALLENGE;
            _Pkt << (std::uint8_t) 0x00;
            _Pkt << (std::uint8_t) WOW_FAIL_VERSION_INVALID;
            sLog.outDebug("%d %u is not a valid client version!", PeerID_, Build_);
            break;
        }

        //---- SRP6 验证 -----------------------------------------------------------
        BigNumber _A;

        _A.SetBinary(_Lp->A, 32);

        // SRP 安全检查: A必须不等于0
        if (_A.IsZero()) {
            sLog.outError("%d SRP Check, A is zero", PeerID_);
            _Error = true;
            break;
        }

        Sha1Hash _Sha;
        _Sha.UpdateBigNumbers(&_A, &B_, NULL);
        _Sha.Finalize();
        BigNumber _U;
        _U.SetBinary(_Sha.digest(), 20);
        BigNumber _S = (_A * (v_.ModExp(_U, N_))).ModExp(b_, N_);

        std::uint8_t _VK[40];
        SRP6::M(_S, _VK, sizeof(_VK));
        K_.SetBinary(_VK, 40);

        std::uint8_t _Hash[20];

        _Sha.Initialize();
        _Sha.UpdateBigNumbers(&N_, NULL);
        _Sha.Finalize();
        memcpy(_Hash, _Sha.digest(), 20);
        _Sha.Initialize();
        _Sha.UpdateBigNumbers(&g_, NULL);
        _Sha.Finalize();
        for (int _I = 0; _I < 20; ++_I) {
            _Hash[_I] ^= _Sha.digest()[_I];
        }
        BigNumber _T3;
        _T3.SetBinary(_Hash, 20);

        _Sha.Initialize();
        _Sha.UpdateData(LoginName_);
        _Sha.Finalize();
        std::uint8_t _T4[SHA_DIGEST_LENGTH];
        memcpy(_T4, _Sha.digest(), SHA_DIGEST_LENGTH);

        _Sha.Initialize();
        _Sha.UpdateBigNumbers(&_T3, NULL);
        _Sha.UpdateData(_T4, SHA_DIGEST_LENGTH);
        _Sha.UpdateBigNumbers(&s_, &_A, &B_, &K_, NULL);
        _Sha.Finalize();
        BigNumber _M;
        _M.SetBinary(_Sha.digest(), 20);

        if (!memcmp(_M.AsByteArray(), _Lp->M1, 20))
        {
            /* 验证成功 */
            sLog.outDebug("%d User '%s' successfully authenticated", PeerID_, LoginName_.c_str());

            /* 更新帐号会话KEY，最后登录IP，最后登录时间，登录区域，失败登陆次数 */
            const char* _Khex = K_.AsHexStr();
            static SqlStatementID _UpdateProof;
            SqlStatement _Stmt = sLoginDB.CreateStatement(_UpdateProof,
                "UPDATE account SET "
                "sessionkey = ?, "
                "last_ip = ?, "
                "last_login = NOW(), "
                "locale = ?, "
                "failed_logins = 0 WHERE username = ?");
            _Stmt.PExecute(_Khex, PeerHost_.c_str(), (std::uint32_t)GetLocaleByName(LocalizationName_),
                SafeLoginName_.c_str());
            BigNumber::Free((void*)_Khex);

            /* 向客户端发送SRP6验证数据 */
            _Sha.Initialize();
            _Sha.UpdateBigNumbers(&_A, &_M, &K_, NULL);
            _Sha.Finalize();
            _SendProof(_Sha);

            /* 设置为已通过验证 */
            Authed_ = true;

            break;
        }

        /* 验证失败 */
        ByteBuffer _Pkt;
        _Pkt << (std::uint8_t)CMD_AUTH_LOGON_PROOF;
        _Pkt << (std::uint8_t)WOW_FAIL_UNKNOWN_ACCOUNT;
        Sock_->SendPacket(_Pkt);

        sLog.outDebug("%d account %s tried to login with wrong password", PeerID_, LoginName_.c_str());

        _HandleBanned();
    } while (false);

    return !_Error;
}

bool AuthSession::HandleReconnectChallenge(AuthPacket* _Packet)
{
    sAuthLogonChallenge_C* _Ch = (sAuthLogonChallenge_C*)_Packet->contents();
    LoginName_.assign((const char*)_Ch->I, _Ch->I_len);
    SafeLoginName_ = LoginName_;
    sLoginDB.EscapeString(SafeLoginName_);
    Build_ = _Ch->Build;

    sLoginDB.AsyncPQuery(&G_AuthHandler_, &AuthHandler::HandleReconnectChallengeCallback,
        PeerID_, "SELECT sessionkey, id FROM account WHERE username = '%s'", SafeLoginName_.c_str());
    return true;
}

void AuthSession::HandleReconnectChallenge(QueryResult* _Result)
{
    /* 检查帐号是否存在 */
    if (!_Result)
    {
        sLog.outDebug("%d user %s tried to login and we can not find his session key in the database.", PeerID_, LoginName_.c_str());
        Running_ = false;
        return;
    }

    AccountId_ = (*_Result)[1].AsUInt32();
    K_.SetHexStr((*_Result)[0].c_str());

    /* 发送响应数据 */
    ByteBuffer _Pkt;
    _Pkt << (std::uint8_t)  CMD_AUTH_RECONNECT_CHALLENGE;
    _Pkt << (std::uint8_t)  0x00;
    ReconnectProof_.SetRand(16 * 8);
    _Pkt.Append(ReconnectProof_.AsByteArray(16), 16);
    _Pkt << (std::uint64_t) 0x00 << (::std::uint64_t) 0x00;

    Sock_->SendPacket(_Pkt);
}

bool AuthSession::HandleReconnectProof(AuthPacket* _Packet)
{
    sAuthReconnectProof_C* _Lp = (sAuthReconnectProof_C*)_Packet->contents();

    BigNumber _T1;
    _T1.SetBinary(_Lp->R1, 16);

    Sha1Hash _Sha;
    _Sha.Initialize();
    _Sha.UpdateData(LoginName_);
    _Sha.UpdateBigNumbers(&_T1, &ReconnectProof_, &K_, NULL);
    _Sha.Finalize();

    if (!memcmp(_Sha.digest(), _Lp->R2, SHA_DIGEST_LENGTH))
    {
        /* 发送响应 */
        ByteBuffer _Pkt;
        _Pkt << (std::uint8_t)  CMD_AUTH_RECONNECT_PROOF;
        _Pkt << (std::uint8_t)  0x00;
        Sock_->SendPacket(_Pkt);

        /* 设置为已通过验证 */
        Authed_ = true;
        return true;
    }
    else
    {
        /* 验证失败，关闭连接 */
        sLog.outDebug("%d user %s tried to login, but session invalid.", PeerID_, LoginName_.c_str());
        return false;
    }
}

bool AuthSession::HandleRealmList(AuthPacket*)
{
    sLoginDB.AsyncPQuery(&G_AuthHandler_, &AuthHandler::HandleFetchCharaNumCallback,
        PeerID_, "SELECT realmid, numchars FROM realmcharacters WHERE acctid = %d;", AccountId_);
    return true;
}

void AuthSession::HandleRealmList(QueryResult* _Result)
{
    /* 加载服务器列表 */
    ByteBuffer _Rmpkt;
    _LoadRealmlist(_Result, _Rmpkt);

    ByteBuffer _Pkt;
    _Pkt << (std::uint8_t)CMD_REALM_LIST;
    _Pkt << (std::uint16_t)_Rmpkt.size();
    _Pkt.Append(_Rmpkt);

    Sock_->SendPacket(_Pkt);
}

void AuthSession::_HandleBanned(void)
{
    const struct Configuration::Any& _RealmdConf = APPLICATION_CONF("Realmd");
    int _MaxWrongPass = _RealmdConf["MaxWrongPass"].As<int>();
    if (_MaxWrongPass > 0)
    {
        /* 更新帐号登录失败次数 */
        static SqlStatementID _UpdateFailedLogins;
        SqlStatement _Stmt = sLoginDB.CreateStatement(_UpdateFailedLogins,
            "UPDATE account SET failed_logins = ? WHERE username = ?");
        _Stmt.PExecute(++FailedLogins_, SafeLoginName_.c_str());

        if (FailedLogins_ >= (std::uint32_t)_MaxWrongPass)
        {
            std::uint32_t _WrongPassBanTime = _RealmdConf["WrongPassBanTime"].As<std::uint32_t>();
            int _WrongPassBanType = _RealmdConf["WrongPassBanType"].As<int>();

            if (_WrongPassBanType == 0)
            {
                /* 禁止帐号登录 */
                static SqlStatementID _InsertAccountBanned;
                SqlStatement _Stmt = sLoginDB.CreateStatement(_InsertAccountBanned,
                    "INSERT INTO account_banned VALUES (?,UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+?,'node realmd','Failed login autoban',1)");
                _Stmt.PExecute(SafeLoginName_.c_str(), _WrongPassBanTime);
                sLog.outDebug("account %s got banned for '%u' seconds because it failed to authenticate '%u' times",
                    LoginName_.c_str(), _WrongPassBanTime, FailedLogins_);
            }
            else
            {
                /* 禁止IP登录 */
                std::string _Address = PeerHost_;
                sLoginDB.EscapeString(_Address);
                static SqlStatementID _InsertIpBanned;
                SqlStatement _Stmt = sLoginDB.CreateStatement(_InsertIpBanned,
                    "INSERT INTO ip_banned VALUES (?,UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+?,'MaNGOS realmd','Failed login autoban')");
                _Stmt.PExecute(PeerHost_.c_str(), _WrongPassBanTime);
                sLog.outDebug("IP %s got banned for '%u' seconds because account %s failed to authenticate '%u' times",
                    PeerHost_.c_str(), _WrongPassBanTime, LoginName_.c_str(), FailedLogins_);
            }
        }
    }
}

void AuthSession::_SetVSFields(const std::string& _RI)
{
    /* 计算v,s */
    s_.SetRand(s_BYTE_SIZE * 8);

    BigNumber _I;
    _I.SetHexStr(_RI.c_str());

    // In case of leading zeros in the rI hash, restore them
    std::uint8_t _Digest[SHA_DIGEST_LENGTH];
    memset(_Digest, 0, SHA_DIGEST_LENGTH);
    if (_I.GetNumBytes() <= SHA_DIGEST_LENGTH) {
        memcpy(_Digest, _I.AsByteArray(), _I.GetNumBytes());
    }

    std::reverse(_Digest, _Digest + SHA_DIGEST_LENGTH);

    Sha1Hash _Sha;
    _Sha.UpdateData(s_.AsByteArray(), s_.GetNumBytes());
    _Sha.UpdateData(_Digest, SHA_DIGEST_LENGTH);
    _Sha.Finalize();
    BigNumber _X;
    _X.SetBinary(_Sha.digest(), _Sha.length());
    v_ = g_.ModExp(_X, N_);

    // 把v,s保存到数据库
    const char* _Vhex, *_Shex;
    _Vhex = v_.AsHexStr();
    _Shex = s_.AsHexStr();
    static SqlStatementID _UpdateVS;
    SqlStatement _Stmt = sLoginDB.CreateStatement(_UpdateVS, "UPDATE account SET v = ?, s = ? WHERE username = ?");
    _Stmt.PExecute(_Vhex, _Shex, SafeLoginName_.c_str());
    BigNumber::Free((void*)_Vhex);
    BigNumber::Free((void*)_Shex);
}

void AuthSession::_SendProof(Sha1Hash& _Sha)
{
    sAuthLogonProof_S_BUILD_6005 _Proof;
    memcpy(_Proof.M2, _Sha.digest(), 20);
    _Proof.Cmd = CMD_AUTH_LOGON_PROOF;
    _Proof.Error = 0;
    _Proof.Unk2 = 0x00;

    ByteBuffer _Pkt;
    _Pkt.Append((std::uint8_t*)&_Proof, sizeof(sAuthLogonProof_S_BUILD_6005));

    Sock_->SendPacket(_Pkt);
}

void AuthSession::_LoadRealmlist(QueryResult* _Result, ByteBuffer& _Pkt)
{
    RealmList::RealmArray _Realms = sRealmList.GetRealms();

    _Pkt << std::uint32_t(0);
    _Pkt << std::uint8_t(_Realms.size());   // 总服务器数量

    // 该帐号在各服务器角色数量
    typedef boost::unordered_map<std::uint32_t, std::uint8_t>   CharaNumMap;
    CharaNumMap _CharaNums;
    if (_Result) {
        do
        {
            _CharaNums[(*_Result)[0].AsUInt32()] = (*_Result)[1].AsUInt8();
        } while (_Result->NextRow());
    }

    // 构造服务器列表
    RealmList::RealmArray::iterator _Iter = _Realms.begin();
    for (; _Iter != _Realms.end(); ++_Iter)
    {
        std::uint8_t _Chars = 0;
        CharaNumMap::const_iterator _CharaNumIter = _CharaNums.find(_Iter->Id);
        if (_CharaNumIter != _CharaNums.end()) {
            _Chars = _CharaNumIter->second;
        }

        _Pkt << std::uint32_t(_Iter->Icon);            /* icon */
        _Pkt << std::uint8_t(_Iter->Flags);            /* realm flag */
        _Pkt << _Iter->Name;                           /* 服务器名 */
        _Pkt << _Iter->Address;                        /* 服务器地址 */
        _Pkt << _Iter->PopulationLevel;                /* population */
        _Pkt << _Chars;                                /* 帐号在该服务器的角色数量 */
        _Pkt << _Iter->Timezone;                       /* 时区 */
        _Pkt << std::uint8_t(0x00);                     /* unk */
    }

    _Pkt << std::uint16_t(0x0002);
}

void AuthSession::_Uninitialize(void)
{
    FREE_QUEUED_PACKETS(Packets_, AuthPacket);
}
